using System.Runtime.InteropServices;
using WindowsAPI;
using WindowsAPI.PE;
using WindowsAPI.Service;

namespace System.IO
{
    public static class FileInfoEx
    {
        [CopyRight("http://www.codeproject.com/Articles/23166/A-Fast-Way-to-Get-at-the-File-s-Version")]
        public static VS_FIXEDFILEINFO GetFixedFileInfo(this FileInfo file)
        {
            if(!file.Exists)
                throw new FileNotFoundException(file.FullName);

            var result = default(VS_FIXEDFILEINFO);
            //string sVersion = null;

            var hFile = Kernel32.CreateFile(file.FullName, eFileAccess.GenericRead, eFileShare.Read, IntPtr.Zero,
                                            eCreationType.OpenExisting, eFileAttributes.Normal, IntPtr.Zero);

            if(hFile.IsInvalid)
                Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());

            var hFileMapping = Kernel32.FileMapping.CreateFileMapping(hFile);
            //, IntPtr.Zero, FileMapProtection.PageReadonly, 0, 0, null);

            if(hFileMapping == IntPtr.Zero)
            {
                var LastWin32Error = Marshal.GetLastWin32Error();
                Kernel32.CloseHandle(hFile);
                Marshal.ThrowExceptionForHR(LastWin32Error);
            }

            var pMappedFileBase = Kernel32.FileMapping.MapViewOfFile(hFileMapping, FileMapAccess.FileMapRead, 0, 0, 0);

            if(pMappedFileBase.ToInt32() == 0)
            {
                var LastWin32Error = Marshal.GetLastWin32Error();
                Kernel32.CloseHandle(hFileMapping);
                Kernel32.CloseHandle(hFile);
                Marshal.ThrowExceptionForHR(LastWin32Error);
            }

            var DOSHeader = (IMAGE_DOS_HEADER)Marshal.PtrToStructure(pMappedFileBase, typeof(IMAGE_DOS_HEADER));

            if(DOSHeader.Magic == IMAGE_DOS_HEADER.IMAGE_DOS_SIGNATURE)
            {
                var hModule = new IntPtr(pMappedFileBase.ToInt32() + 1);

                const int RT_VERSION = 16;
                var hRes = Kernel32.FindResource(hModule, "#1", RT_VERSION);

                if(hRes != IntPtr.Zero)
                {
                    var hGlobal = Kernel32.LoadResource(hModule, hRes);

                    if(hGlobal != IntPtr.Zero)
                    {
                        var lpRes = Kernel32.LockResource(hGlobal);

                        if(lpRes != IntPtr.Zero)
                        {
                            var VersionInfo = (VS_VERSIONINFO)Marshal.PtrToStructure(lpRes, typeof(VS_VERSIONINFO));

                            if(VersionInfo.wValueLength > 0)
                            {
                                var pFI = new IntPtr(lpRes.ToInt32() + VS_VERSIONINFO.nPaddingOffset);

                                while((pFI.ToInt32() & 0x04) != 0)
                                    pFI = new IntPtr(pFI.ToInt32() + 2);

                                result = (VS_FIXEDFILEINFO)Marshal.PtrToStructure(pFI, typeof(VS_FIXEDFILEINFO));

                                //var v1 = (result.dwFileVersionMS & 0xffff0000) >> 16;
                                //var v2 = result.dwFileVersionMS & 0x0000ffff;
                                //var v3 = (result.dwFileVersionLS & 0xffff0000) >> 16;
                                //var v4 = result.dwFileVersionLS & 0x0000ffff;

                                //sVersion = v1.ToString() + '.' + v2.ToString() + '.' +
                                //           v3.ToString() + '.' + v4.ToString();
                            }
                        }
                    }

                }
            }

            Kernel32.FileMapping.UnmapViewOfFile(pMappedFileBase);
            Kernel32.CloseHandle(hFileMapping);
            Kernel32.CloseHandle(hFile);

            return result;
            //return sVersion;

        }
    }
}